package edu.uj.petri.view;

import edu.uj.petri.commands.Memento;
import edu.uj.petri.commands.PlaceNameDialog;
import edu.uj.petri.commands.RemovePlaceCommand;
import edu.uj.petri.commands.SetTokenCountDialogCommand;
import edu.uj.petri.document.Document;
import edu.uj.petri.model.Node;
import edu.uj.util.Observer;
import edu.uj.petri.model.Place;
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.Set;

/**
 * @author Pawel
 */
public class PlaceView extends NodeView implements Observer {
    /**
     * radius of the circle (in Units)
     */
    public static final double radius = 15;
    
    private static Point2D labelRelativePosition = new Point2D.Double( 5, 25 );

    private Place place;

    private Label label;

    private TokenView tokenView;

    public PlaceView(Place place, Point2D position) {
        
        this.place = place;
        this.position = position;
        place.subscribe(this);
        
        String s = place.getName();
        label = new Label( new Point2D.Double( position.getX() + labelRelativePosition.getX(), position.getY() + labelRelativePosition.getY()), s );
        tokenView = new TokenView( (Point2D)position.clone() );        
    }

    /** checks if point is in circle or in label */
    public boolean containsPoint(Point2D point) {
        
        return (new Ellipse2D.Double( position.getX() - radius, position.getY() - radius, 
            2*radius, 2*radius)).contains( point.getX(), point.getY() );
    }

    public boolean crossesRectangle(Rectangle2D rect) {
        
        return (new Ellipse2D.Double( position.getX() - radius, position.getY() - radius, 
            2*radius, 2*radius)).intersects( rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
            
    }

    public void draw(Canvas canvas) {
        
        Color color;
       
        if (selected) color = SELECTED_COLOR;
        else color = UNSELECTED_COLOR;
        
        canvas.drawCircle( 
                new Rectangle2D.Double( 
                        position.getX() - radius, 
                        position.getY() - radius, 
                        2*radius, 2*radius),
                
                color,
                false);

        label.draw(canvas);
        tokenView.draw(canvas);
    }

    public void setSelected(boolean selected) {
        
        this.selected = selected;
        label.selected = selected;
    }

    public boolean canBeMoved(Point2D startPoint) {
        return containsPoint(startPoint);
    }

    public void move(Point2D start, Point2D end) {
        
        Point2D difference = new Point2D.Double( start.getX() - end.getX(), start.getY() - end.getY());
        position.setLocation( position.getX() + difference.getX(), position.getY() + difference.getY());
        label.move( start, end );
        tokenView.move(start, end);
    }

    public void doubleClick( Point2D point ) {
        // has no effect
    }

    /**
     * items: set number of tokens, delete, change label
     */
    public ContextMenu getContextMenu() {
        
        ContextMenu menu = new ContextMenu();        
        
        // TODO odkomentować
        menu.addItem(ResourceBundle.getBundle("edu/uj/petri/messages").getString("Set_number_of_tokens"), new SetTokenCountDialogCommand(place));
        menu.addItem(ResourceBundle.getBundle("edu/uj/petri/messages").getString("Rename"), new PlaceNameDialog( place ));
        menu.addItem(ResourceBundle.getBundle("edu/uj/petri/messages").getString("Remove"), new RemovePlaceCommand(this));
       
        return menu;
    }

    public void addToken() {
        // change to Command
        place.addTokens(1);
        tokenView.setTokenCount( place.getTokens() );
        observerList.notifyAllSubscribers( REDRAW );
    }

    public void removeToken() {
        // change to Command
        place.deleteTokens(1);
        tokenView.setTokenCount( place.getTokens() );
        observerList.notifyAllSubscribers( REDRAW );
    }

    /** returns token count in place */
    public int getTokenCount() {
        return place.getTokens();
    }

    public void setTokenCount(int tokens) {
        
        place.setTokens(tokens);
        tokenView.setTokenCount( place.getTokens() );
        observerList.notifyAllSubscribers( REDRAW );
    }

    public void setLabel(String text) {
        
        this.label.setText( text );
    }

    /**
     * this method is called when Observed wants to notify all its Observers of 
     *    change in its state.
     * @param eventCode type of event that caused the notification. Observed classes 
     *    should define their unique codes as public final static fields.
     */
    public void notifyFromObserved(int eventCode) {
        // handle events from Place
        switch (eventCode) {
            case Place.TOKENS:
                tokenView.setTokenCount(place.getTokens());
                observerList.notifyAllSubscribers( REDRAW );
                
                break;
            case Place.NAME_CHANGE:
                label.setText(place.getName());
                observerList.notifyAllSubscribers( REDRAW );
                
                break;
        }
    }

    /**
     * template method
     */
    protected void organiseArcs() {
        /*
         * foreach arc
         *    arc->get arc segment near this place
         *    move linking point in segment near this place
         */
    }

    public void removeFrom(View view) {
        
        removed = true;
        
        view.removeElement( this );
        view.removeElement( label );
        view.removeElement( tokenView );
        
        Document.getActiveDocument().getModel().deletePlace( place );
        
        for( ArcView arc : arcs) arc.removeFrom( view );
        
        observerList.notifyAllSubscribers( REDRAW );
        

    }

    public Node getNode() {
        return place;
    }

    public Memento getMemento() {
        PlaceViewMemento retval = new PlaceViewMemento();
        retval.placeMemento = place.getMemento();
        retval.arcs = new HashSet<ArcView>(arcs);
        retval.removed = removed;
        retval.labelMemento = label.getMemento();
        retval.tokenViewMemento = tokenView.getMemento();
        retval.position = (Point2D)position.clone();
        return retval;
    }

    public void applyMemento(Memento memento) {
        PlaceViewMemento myMemento = (PlaceViewMemento)memento;
        place.applyMemento(myMemento.placeMemento);
        label.applyMemento(myMemento.labelMemento);
        tokenView.applyMemento(myMemento.tokenViewMemento);
        position = (Point2D)myMemento.position.clone();
        arcs.clear();
        arcs.addAll(myMemento.arcs);
        removed = myMemento.removed;
        
        // TODO remove - dirty hack
        View view = Document.getActiveDocument().getView();
        for (ArcView arc : arcs) {
            arc.removed = false;
            view.addElement(arc);
        }
    }
    
    static class PlaceViewMemento extends Memento {
        private Memento placeMemento, labelMemento, tokenViewMemento;
        private Set<ArcView> arcs;
        private boolean removed;
        private Point2D position;
    }
    
    private static final long serialVersionUID = 6850912506647681447L;
}
